package framework.dao
{
import com.adobe.serialization.json.JSON;

import flash.geom.Point;
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

public class EntryUtil
{
    private static var ENTRY_CLASS_DESC:Dictionary = new Dictionary(true);
    
	public static function mapping(source:*, entryClass:Class, parent:* = null):*
	{
		var data:* = source;
		if (data is String)
		{
			data = XML(data);
		}
		var entry:* = new entryClass();
        
        var classStr:String = getQualifiedClassName(entryClass);
        if (!ENTRY_CLASS_DESC[classStr])
        {
            ENTRY_CLASS_DESC[classStr] = describeType(entry);
        }
        
		var doc:XML = ENTRY_CLASS_DESC[classStr];
		
		parse(entry, doc.accessor, data, source, parent);
		parse(entry, doc.variable, data, source, parent);
		
		return entry;
	}
	
	private static function parse(entry:*, doc:*, data:*, source:*, parent:*):void
	{
		for each (var item:* in doc)
		{
			for each (var md:* in item.metadata)
			{
				if (md.@name != "Data") continue;
				
				var type:String = md.arg.(@key == "type").@value;
				if (type == "field")
				{
					var key:String = String(item.@name);
					var xmlKey:String = String(md.arg.(@key == "mapping").@value);
					var method:String = String(md.arg.(@key == "method").@value);
					
					if (xmlKey == "*")
					{
						var tempData:* = (data is XML || data is XMLList) ? data.children() : data;
						for each (var obj:* in tempData)
						{
							entry[key] = obj;
						}
						
					}
					else
					{
						var value:*;
						var tempValue:* = data;
						var xmlKeyArray:Array = xmlKey.split(".");
						for each (var tempXMLKey:String in xmlKeyArray)
						{
							tempValue = tempValue[tempXMLKey];
						}
						value = tempValue;
						if (entry[key] is Boolean)
						{
							if (String(value) != "")
							{
								if (isNaN(Number(value)))
								{
									entry[key] = String(value).toLowerCase() == "true" ? true : false;
								}
								else
								{
									entry[key] = int(value) == 1 ? true : false;
								}
							}
						}
						else if (entry[key] is Point)
						{
							if (String(value) != "")
							{
								var p:Point = new Point();
								var pstr:String = String(value);
								p.x = pstr.split(",")[0];
								p.y = pstr.split(",")[1];
								entry[key] = p;
							}
							else
							{
								entry[key] = new Point(0, 0);
							}
						}
						else
						{
							if (method && method !== "")
							{
								entry[key] = EntryUtil[method](value);
							}
							else
							{
								entry[key] = value;
							}
						}
					}
				}
				else if (type == "mapping")
				{
					var mapping:String = md.arg.(@key == "mapping").@value;
					var way:String = md.arg.(@key == "way").@value;
					var ref:String = md.arg.(@key == "ref").@value;
					var crf:String;
					var object:String = md.arg.(@key == "object").@value;
					var objectKey:String = md.arg.(@key == "key").@value;
					var preHandler:String = md.arg.(@key == "preHandler").@value;
					
					var mappingArr:Array = mapping.split(".");
					var source:* = data;
					for each (var property:String in mappingArr)
					{
						source = source[property];
					}
					
					if (way == "one_to_more")
					{
						var setStr:String = String(md.arg.(@key == "set").@value);
						var paramsStr:String = String(md.arg.(@key == "params").@value);
						
						var set:Array = setStr == "" ? [] : setStr.split(",");    
						var params:Array = paramsStr == "" ? [] : paramsStr.split(",");      
						
						var list:*;
						if (object && object != "")
						{
							var listRef:Class = getDefinitionByName(object) as Class;
							list = new listRef();
						}
						else
						{
							list = [];
						}
						for each (var d:* in source)
						{	
							crf = ref;
							if (crf != null && crf.indexOf("$") == 0)
							{
								crf = crf.substr(1);
								crf = d[crf];
							}
							var ir:* = EntryUtil.mapping(d, getDefinitionByName(crf) as Class, entry);	
							if (preHandler && preHandler != "")
							{
								entry[preHandler](ir);
							}
							for (var i:int = 0; i < set.length; i++)
							{
								ir[set[i]] = params[i] == "this" ? entry : entry[params[i]];
							}
							if (list is Array)
							{
								list.push(ir);
							}
							else
							{
								list[String(d[objectKey])] = ir;
							}
						}
						entry[String(item.@name)] = list;
					}
					else if (way == "more_to_one")
					{
						if (checkClassType(doc, String(item.@name), parent) && 
							String(md.arg.(@key == "ignoreError").@value) == "true")
						{
							entry[String(item.@name)] = parent;
						}
					}
					else if (way == "own")
					{
						crf = ref;
						if (crf != null && crf.indexOf("$") == 0)
						{
							crf = crf.substr(1);
							crf = source[crf];
						}
						entry[String(item.@name)] = EntryUtil.mapping(source, getDefinitionByName(crf) as Class, entry);
					}
					else if (way == "belong")
					{
						entry[String(item.@name)] = parent;
					}
				}
			}
		}
	}
    
    private static function checkClassType(doc:XML, property:String, target:*):Boolean
    {
        var obj:* = doc.accessor.(@name == property);
        var ref:String = String(obj.@type);
        ref = ref.replace("::", ".");
        var classRef:Class = getDefinitionByName(ref) as Class;
        return (target is classRef);
    }
	
	private static function json(source:String):Object
	{
		return JSON.decode(source);
	}
	
	private static function stringToArray(source:String):Array
	{
		return (!source || source == "") ? [] : source.split(",");
	}
}
}